home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / ui.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  17KB  |  646 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. #ifndef lint
  9. static char *RCSid = "$Header: /tmp_mnt/net/quake/proj/wais/wais-8-b5/ir/RCS/ui.c,v 1.16 92/03/18 09:01:09 jonathan Exp $";
  10. #endif
  11.  
  12. /* Change log:
  13.  * $Log:    ui.c,v $
  14.  * Revision 1.16  92/03/18  09:01:09  jonathan
  15.  * don't free DocObjs[0]->Type, it's a copy of the input!
  16.  * 
  17.  * Revision 1.15  92/03/06  12:53:03  jonathan
  18.  * Plug another memory leak.  From ericb@baker.dartmouth.edu (Eric J Bivona).
  19.  * 
  20.  * Revision 1.14  92/02/14  15:26:53  jonathan
  21.  * Conditionalized interpret_message and locally_answer_message so client need
  22.  * not include search engine.
  23.  * 
  24.  * Revision 1.13  92/02/12  13:53:41  jonathan
  25.  * Added "$Log" so RCS will put the log message in the header
  26.  * 
  27.  * 
  28. */
  29.  
  30. /* 
  31.  * this is a simple ui toolkit for building other ui's on top.
  32.  * -brewster
  33.  * 
  34.  * top level functions:
  35.  *   generate_search_apdu
  36.  *   generate_retrieval_apdu
  37.  *   interpret_message
  38.  *
  39.  */
  40.  
  41. /* to do:
  42.  *   generate multiple queries for long documents.
  43.  *     this will crash if the file being retrieved is larger than 100k.
  44.  *   
  45.  */
  46.  
  47. #include "ui.h"
  48. #include "wutil.h"
  49. #include "ustubs.h"
  50. #include "futil.h"
  51.  
  52. #include <ctype.h>
  53. #include <errno.h>
  54. #include <string.h>
  55. #include <sys/types.h>
  56.  
  57. /* returns a pointer in the buffer of the first free byte.
  58.    if it overflows, then NULL is returned 
  59.  */
  60. char *
  61. generate_search_apdu(buff,
  62.              buff_len,
  63.              seed_words,
  64.              database_name,
  65.              docobjs,
  66.              maxDocsRetrieved)
  67. char* buff;     /* buffer to hold the apdu */
  68. long *buff_len;    /* length of the buffer changed to reflect new data written */
  69. char *seed_words;    /* string of the seed words */
  70. char *database_name;
  71. DocObj** docobjs;
  72. long maxDocsRetrieved;
  73. {
  74.   /* local variables */
  75.  
  76.   SearchAPDU *search3;
  77.   char  *end_ptr;
  78.   static char *database_names[2] = {"", 0};
  79.   any refID;
  80.   WAISSearch *query;
  81.   refID.size = 1;
  82.   refID.bytes = "3";
  83.  
  84.   database_names[0] = database_name;
  85.   query = makeWAISSearch(seed_words,
  86.                          docobjs, /* DocObjsPtr */
  87.                          0L,
  88.                          1L,     /* DateFactor */
  89.                          0L,     /* BeginDateRange */
  90.                          0L,     /* EndDateRange */
  91.                          maxDocsRetrieved
  92.                          );
  93.  
  94.   search3 = makeSearchAPDU(30L, 
  95.                5000L, /* should be large */
  96.                30L,
  97.                            1L,    /* replace indicator */
  98.                            "",    /* result set name */
  99.                            database_names, /* database name */   
  100.                            QT_RelevanceFeedbackQuery, /* query_type */
  101.                            0L,   /* element name */
  102.                            NULL, /* reference ID */
  103.                            query);
  104.  
  105.   end_ptr = writeSearchAPDU(search3, buff, buff_len);
  106.  
  107.   CSTFreeWAISSearch(query);
  108.   freeSearchAPDU(search3);
  109.   return(end_ptr);
  110. }
  111.  
  112.  
  113. /* returns a pointer into the buffer of the next free byte.
  114.    if it overflowed, then NULL is returned
  115.  */
  116.  
  117. char *
  118.  generate_retrieval_apdu(buff,
  119.             buff_len,
  120.             docID,
  121.             chunk_type,
  122.             start,
  123.             end,
  124.             type,
  125.             database_name)
  126. char *buff;
  127. long *buff_len;    /* length of the buffer changed to reflect new data written */
  128. any *docID;
  129. long chunk_type;
  130. long start;
  131. long end;
  132. char *type;
  133. char *database_name;
  134. {
  135.   SearchAPDU *search;
  136.   char  *end_ptr;
  137.  
  138.   static char *database_names[2];
  139.   static char *element_names[3];
  140.   any refID;
  141.  
  142.   DocObj *DocObjs[2];
  143.   any *query;            /* changed from char* by brewster */
  144.  
  145.   if(NULL == type)
  146.     type = s_strdup("TEXT");
  147.  
  148.   database_names[0] = database_name;
  149.   database_names[1] = NULL;
  150.  
  151.   element_names[0] = " ";
  152.   element_names[1] = ES_DocumentText;
  153.   element_names[2] = NULL;
  154.  
  155.   refID.size = 1;
  156.   refID.bytes = "3";
  157.   
  158.   switch(chunk_type){
  159.   case CT_line: 
  160.     DocObjs[0] = makeDocObjUsingLines(docID, type, start, end);
  161.     break;
  162.   case CT_byte:
  163.     DocObjs[0] = makeDocObjUsingBytes(docID, type, start, end);
  164.     break;
  165.   }
  166.   DocObjs[1] = NULL;
  167.  
  168.   query = makeWAISTextQuery(DocObjs);   
  169.   search = makeSearchAPDU( 10L, 16L, 15L, 
  170.               1L,    /* replace indicator */
  171.               "FOO", /* result set name */
  172.               database_names, /* database name */   
  173.               QT_TextRetrievalQuery, /* query_type */
  174.               element_names, /* element name */
  175.               &refID, /* reference ID */
  176.               query);
  177.   end_ptr = writeSearchAPDU(search, buff, buff_len);
  178.   /* s_free(DocObjs[0]->Type); it's a copy of the input, don't free it! */
  179.   CSTFreeDocObj(DocObjs[0]);
  180.   CSTFreeWAISTextQuery(query);
  181.   freeSearchAPDU(search);
  182.   return(end_ptr);
  183. }
  184.  
  185. /* not currently used 
  186.  
  187. static boolean isnumber _AP((char* string));
  188.  
  189. static boolean isnumber(string)
  190. char *string;
  191. {
  192.   long count;
  193.   for(count = 0; count < strlen(string); count++){
  194.     if(!isdigit(string[count])){
  195.       return(false);
  196.     }
  197.   }
  198.   return(true);
  199. }
  200.  
  201. */
  202.  
  203. /* this will negotiate with server, and returs the maximum buffer size 
  204.    the server can handle.
  205.  
  206.    A connection should be established first using open_socket.
  207.  
  208. */
  209.  
  210. long init_connection(inBuffer, outBuffer, bufferSize, connection, userInfo)
  211. char *inBuffer, *outBuffer;
  212. long bufferSize;
  213. FILE *connection;
  214. char *userInfo;
  215.   InitAPDU* init = NULL;
  216.   InitResponseAPDU* reply = NULL;
  217.   long result;
  218.   /* construct an init */
  219.   init = makeInitAPDU(true,false,false,false,false,bufferSize,bufferSize,
  220.               userInfo,defaultImplementationID(),
  221.               defaultImplementationName(),
  222.               defaultImplementationVersion(),NULL,userInfo);
  223.   /* write it to the buffer */
  224.   result = writeInitAPDU(init,inBuffer+HEADER_LENGTH,&bufferSize) - inBuffer;
  225.  
  226.   if(result < 0){
  227.     freeInitAPDU(init);
  228.     return(-1);
  229.   }
  230.   if(0 ==
  231.      interpret_message(inBuffer,
  232.                result - HEADER_LENGTH,
  233.                outBuffer,
  234.                bufferSize,
  235.                connection,
  236.                false    /* true verbose */    
  237.                )) {
  238.     /* error making a connection */
  239.     return (-1);
  240.   }
  241.   if (readInitResponseAPDU(&reply,outBuffer + HEADER_LENGTH) == NULL){
  242.     freeWAISInitResponse((WAISInitResponse*)reply->UserInformationField);
  243.     freeInitResponseAPDU(reply);
  244.     return(-1);
  245.   }
  246.   if (reply->Result == false)
  247.     {                /* the server declined service */
  248.       freeWAISInitResponse((WAISInitResponse*)reply->UserInformationField);
  249.       freeInitResponseAPDU(reply);
  250.       return(-1);
  251.     }
  252.   else                /* we got a response back */
  253.     { result = reply->MaximumRecordSize;
  254.       freeWAISInitResponse((WAISInitResponse*)reply->UserInformationField);
  255.       freeInitResponseAPDU(reply);
  256.       return(result);
  257.     }
  258. }
  259.  
  260. #ifdef LOCAL_SEARCH
  261. /* returns the length of the response, 0 if an error */
  262. long
  263. locally_answer_message(request_message,
  264.                request_length,
  265.                response_message,
  266.                response_buffer_length)
  267. char *request_message;
  268. long request_length;
  269. char *response_message;
  270. long response_buffer_length;
  271. {
  272.   long request_length_internal = request_length;
  273.   long response_length;
  274.   WAISMessage header;
  275.   long maxBufferSize = response_buffer_length;
  276.  
  277.   readWAISPacketHeader(request_message, &header);
  278.   {
  279.     char length_array[11];
  280.     strncpy(length_array, header.msg_len, 10);
  281.     length_array[10] = '\0';
  282.     request_length_internal = atol(length_array);
  283.   }
  284.   /*
  285.     printf("request length is %ld (%ld from caller)\n", 
  286.     request_length_internal,
  287.     request_length);
  288.     */
  289.   
  290.   response_length =
  291.     interpret_buffer(request_message + HEADER_LENGTH, 
  292.              request_length_internal,
  293.              response_message + HEADER_LENGTH,
  294.              response_buffer_length,
  295.              &maxBufferSize,
  296.              (long)header.hdr_vers,
  297.              NULL);
  298.   if(0 > response_length)
  299.     return(0);
  300.   writeWAISPacketHeader(response_message,
  301.             response_length,
  302.             (long)'z',    /* Z39.50 */
  303.             "DowQuest  ", /* server name */
  304.             (long)NO_COMPRESSION,    /* no compression */
  305.             (long)NO_ENCODING,(long)header.hdr_vers);
  306.   return(response_length);
  307. }
  308. #endif
  309.  
  310. /* this is a safe version of unix 'read' it does all the checking
  311.  * and looping necessary
  312.  * to those trying to modify the transport code to use non-UNIX streams:
  313.  *  This is the function to modify!
  314.  */
  315. long read_from_stream(d,buf,nbytes)
  316. long d;                /* this is the stream */
  317. char *buf;
  318. long nbytes;
  319. {
  320.   long didRead;
  321.   long toRead = nbytes;
  322.   long totalRead = 0;        /* paranoia */
  323.  
  324.   while (toRead > 0){
  325.     didRead = read (d, buf, toRead);
  326.     if(didRead == -1)        /* error*/
  327.       return(-1);
  328.     if(didRead == 0)        /* eof */
  329.       return(-2);        /* maybe this should return 0? */
  330.     toRead -= didRead;
  331.     buf += didRead;
  332.     totalRead += didRead;
  333.   }
  334.   if(totalRead != nbytes)    /* we overread for some reason */
  335.     return(- totalRead);    /* bad news */    
  336.   return(totalRead);
  337. }
  338.  
  339. /* returns the length of the response, 0 if an error */
  340.  
  341. long 
  342. transport_message(connection,
  343.           request_message,
  344.           request_length,
  345.           response_message,
  346.           response_buffer_length)
  347. FILE *connection;
  348. char *request_message;
  349. long request_length;
  350. char *response_message;
  351. long response_buffer_length;
  352. {
  353.   WAISMessage header;
  354.   long response_length;
  355.  
  356.   
  357.   /* Write out message. Read back header. Figure out response length. */
  358.   
  359.   if( request_length + HEADER_LENGTH
  360.      != fwrite (request_message, 1L, request_length + HEADER_LENGTH, connection))
  361.     return 0;
  362.  
  363.   fflush(connection);
  364.  
  365.   /* read for the first '0' */
  366.  
  367.   while(1){
  368.     if(0 > read_from_stream(fileno(connection), response_message, 1))
  369.       return 0;
  370.     if('0' == response_message[0])
  371.       break;
  372.   }
  373.  
  374.   if(0 > read_from_stream(fileno(connection), 
  375.                   response_message + 1, 
  376.                   HEADER_LENGTH - 1))
  377.     return 0;
  378.  
  379.   readWAISPacketHeader(response_message, &header);
  380.   {
  381.     char length_array[11];
  382.     strncpy(length_array, header.msg_len, 10);
  383.     length_array[10] = '\0';
  384.     response_length = atol(length_array);
  385.     /*
  386.       if(verbose){
  387.       printf("WAIS header: '%s' length_array: '%s'\n", 
  388.       response_message, length_array);
  389.       }
  390.       */
  391.     if(response_length > response_buffer_length){
  392.       /* we got a message that is too long, therefore empty the message out,
  393.      and return 0 */
  394.       long i;
  395.       for(i = 0; i < response_length; i++){
  396.     read_from_stream(fileno(connection), 
  397.              response_message + HEADER_LENGTH,
  398.              1);
  399. /*    fread(response_message + HEADER_LENGTH, 1, 1, connection); */
  400.       }
  401.       return(0);
  402.     }
  403.   }
  404.   if(0 > read_from_stream(fileno(connection), 
  405.                   response_message + HEADER_LENGTH,
  406.                   response_length))
  407. /*  if(0 > fread(response_message + HEADER_LENGTH,
  408.            1, response_length, connection)) */
  409.     return 0;
  410.   return(response_length);
  411. }
  412.  
  413.  
  414. /* ------------------------------------------------------------*/
  415.  
  416. /*  Facility to record messages sent and recieved for testing 
  417.     and timing purposes. */
  418.  
  419. /* from c:
  420.  
  421.    putenv(strdup("IR_FILE=/users/menlo-park/brewster/tmp/infile"));
  422.  
  423.    from csh:
  424.  
  425.    setenv IR_FILE /users/menlo-park/brewster/tmp/infile
  426.  
  427.  */
  428.  
  429. boolean environment_variables_read = false; 
  430. char* ir_file_environment_variable = "IR_FILE";
  431. char* ir_file = NULL;
  432.  
  433. void read_environment_variables(host, port)
  434.      char* host;
  435.      char* port;
  436. {
  437.   
  438.   if(!environment_variables_read){
  439.     FILE *stream;
  440.     ir_file = (char*)getenv(ir_file_environment_variable);
  441.     if(ir_file){
  442.       printf("IR_file: %s\n", ir_file);
  443.       stream = fopen(ir_file, "w");
  444.       fprintf(stream, "%s %s\n", host, port);
  445.       fclose(stream);
  446.     }
  447.     environment_variables_read = true;
  448.   }
  449. }
  450.  
  451. /* returns 0 if success */
  452. long write_message_to_file(message, length, filename)
  453.      unsigned char *message;
  454.      long length;
  455.      char*filename;
  456. {
  457.   FILE *stream = fopen(filename, "a");
  458.   if(NULL == stream)
  459.     return(-1);
  460.   
  461.   printf("Writing to file: %s %d characters\n", filename, length);
  462.  
  463.   fprintf(stream, "---------------------------------\n");
  464.   
  465.   if(length != fwrite(message, sizeof(unsigned char), 
  466.               length, stream)){
  467.     perror("fwrite error");
  468.     fclose(stream);
  469.     return(-2);
  470.   }
  471.  
  472.   fprintf(stream, "\n");
  473.  
  474.   if(0 != fclose(stream))
  475.     return(-3);
  476.   return(0);
  477. }  
  478.  
  479. /* ------------------------------------------------------------*/
  480.  
  481. /* returns the number of bytes writeen.  0 if an error */
  482. long
  483. interpret_message(request_message,request_length,
  484.           response_message,
  485.           response_buffer_length,
  486.           connection,
  487.           verbose)
  488. char *request_message;
  489. long request_length; /* length of the buffer */
  490. char *response_message;
  491. long response_buffer_length;
  492. FILE *connection;
  493. boolean verbose;
  494. {
  495.   long response_length;
  496.  
  497.   writeWAISPacketHeader(request_message,
  498.             request_length,
  499.             (long)'z',    /* Z39.50 */
  500.             "wais      ", /* server name */
  501.             (long)NO_COMPRESSION,    /* no compression */
  502.             (long)NO_ENCODING,(long)HEADER_VERSION);
  503.  
  504.   if(ir_file){
  505.     long error_code;
  506.     if(0 != (error_code = 
  507.          write_message_to_file((unsigned char*)request_message, 
  508.                    request_length + HEADER_LENGTH, 
  509.                    ir_file)))
  510.       printf("Error writing log file Code: %d\n", error_code);
  511.   }
  512.     
  513.   if(connection != NULL) {
  514.     if(0 == 
  515.        (response_length =
  516.     transport_message(connection, request_message,
  517.               request_length,
  518.               response_message,
  519.               response_buffer_length)))
  520.       return(0);
  521.   }
  522.   else{
  523. #ifdef LOCAL_SEARCH    
  524.     if(0 == 
  525.        (response_length =
  526.     locally_answer_message(request_message, request_length, 
  527.                    response_message,
  528.                    response_buffer_length)))
  529.       return(0);
  530. #else
  531.     waislog(WLOG_HIGH, WLOG_ERROR, "Local search not supported in this version");
  532.     return(0);
  533. #endif
  534.   }
  535.   if(verbose){
  536.     printf ("decoded %ld bytes: \n", response_length);
  537.     twais_dsply_rsp_apdu(response_message + HEADER_LENGTH, 
  538.              request_length);
  539.   }
  540.   if(ir_file){
  541.     long error_code;
  542.     if(0 != (error_code = 
  543.          write_message_to_file((unsigned char*)response_message, 
  544.                    response_length + HEADER_LENGTH, 
  545.                    ir_file)))
  546.       printf("Error writing log file Code: %d\n", error_code);
  547.   }
  548.   return(response_length);
  549. }
  550.  
  551. /* this closes the connection to the socket.
  552.  * the mythology is that this is cleaner than exiting
  553.  */
  554.  
  555. long close_connection(connection)
  556. FILE *connection;
  557. {
  558.   long result = 0;
  559.   
  560.   if(connection != NULL){
  561.     result = fclose(connection);
  562.   }
  563.   return(result);
  564. }
  565.   
  566. void
  567. display_text_record_completely(record,quote_string_quotes)
  568. WAISDocumentText *record;
  569. boolean quote_string_quotes;
  570. {
  571.   long count;
  572.   /* printf(" Text\n");
  573.      print_any("     DocumentID:  ", record->DocumentID);
  574.      printf("     VersionNumber:  %d\n", record->VersionNumber);
  575.      */
  576.   for(count = 0; count < record->DocumentText->size; count++){
  577.     long ch = (unsigned char)record->DocumentText->bytes[count];
  578.     if(27 == ch){
  579.       /* then we have an escape code */
  580.       /* if the next letter is '(' or ')', then ignore two letters */
  581.       if('(' == record->DocumentText->bytes[count + 1] ||
  582.      ')' == record->DocumentText->bytes[count + 1])
  583.     count += 1;             /* it is a term marker */
  584.       else count += 4;        /* it is a paragraph marker */
  585.     }
  586.     else if (ch == '\t') /* a TAB! */
  587.       putc(ch, stdout);
  588.     else if (isprint(ch)){
  589.       if(quote_string_quotes && ch == '"')
  590.     putc('\\', stdout);
  591.       putc(ch, stdout);
  592.     } 
  593.     else if (ch == '\n' || ch == '\r')
  594.       printf ("\n");
  595.   }
  596. }
  597.  
  598. /* modifies the string to exclude all seeker codes. sets length to
  599.    the new length. */
  600. char *delete_seeker_codes(string,length)
  601. char *string;
  602. long *length;
  603. {
  604.   long original_count; /* index into the original string */
  605.   long new_count = 0; /* index into the collapsed string */
  606.   for(original_count = 0; original_count < *length; original_count++){
  607.     if(27 == string[original_count]){
  608.       /* then we have an escape code */
  609.       /* if the next letter is '(' or ')', then ignore two letters */
  610.       if('(' == string[original_count + 1] ||
  611.     ')' == string[original_count + 1])
  612.      original_count += 1;    /* it is a term marker */
  613.       else original_count += 4; /* it is a paragraph marker */
  614.     }
  615.     else string[new_count++] = string[original_count];
  616.   }
  617.   *length = new_count;
  618.   return(string);
  619. }
  620.   
  621.  
  622. /* returns a pointer to a string with good stuff */
  623. char *trim_junk(headline)
  624. char *headline;
  625. {
  626.   long length = strlen(headline) + 1; /* include the trailing null */
  627.   long i;
  628.   headline = delete_seeker_codes(headline, &length);
  629.   /* delete leading spaces */
  630.   for(i=0; i < strlen(headline); i++){
  631.     if(isprint(headline[i])){
  632.       break;
  633.     }
  634.   }
  635.   headline = headline + i;
  636.   /* delete trailing stuff */
  637.   for(i=strlen(headline) - 1 ; i > 0; i--){
  638.     if(isprint(headline[i])){
  639.       break;
  640.     }
  641.     headline[i] = '\0';
  642.   }
  643.   return(headline);
  644. }
  645.